home *** CD-ROM | disk | FTP | other *** search
- ; CPSUTL.ASM
- ; KERMIT - (Celtic for "FREE")
- ;
- ; This is the CP/M-80 implementation of the Columbia University
- ; KERMIT file transfer protocol.
- ;
- ; Version 4.0
- ;
- ; Copyright June 1981,1982,1983,1984,1985
- ; Columbia University
- ;
- ; Originally written by Bill Catchings of the Columbia University Center for
- ; Computing Activities, 612 W. 115th St., New York, NY 10025.
- ;
- ; Contributions by Frank da Cruz, Daphne Tzoar, Bernie Eiben,
- ; Bruce Tanner, Nick Bush, Greg Small, Kimmo Laaksonen, Jeff Damens, and many
- ; others.
- ;
- ; Utility routines, pure and impure data.
- ;
- ; revision history:
- ;
- ;edit 31, 21-Mar-1991 by MF. Implement edit 30 without checking takflg after
- ; "r1tch1" as we are **always** TAKEing from a file if we get to that
- ; point in the code. Makes for simplicity.
- ;edit 30, 27-Feb-1991 by MF. When TAKEing characters from a TAKE-file,
- ; view semicolons as normal characters (not command separators).
- ; This will allow such commands as REMOTE DELETE *.*;* to work
- ; properly from TAKE-files without having to revert to old code in
- ; cpsrem.asm at "remcl0" to decode Remote command arguments. TAKE-files
- ; ought not (in my opinion) to have multiple commands per line anyway.
- ;edit 29, 30-Jan-1991 by MF. Fix bug in IN1CHR which decremented "chrcnt"
- ; once too often after a call to INBUF (which predecrements it
- ; already). This, along with a fix in CPSTT.ASM, fixes a bug in the
- ; TRANSMIT command wherein certain characters in the file were not
- ; being transmitted. This bug was reported to me by Lance Tagliapietra
- ; of the University of Wisconsin at Platteville, WI (Email:
- ; <TAGLANCE@ucs.UWPLATT.EDU>).
- ; (he suggested not predecrementing "chrcnt" in INBUF" but this
- ; breaks code in routine "GTCHR" from CPSPK1.ASM so it's better
- ; to modify IN1CHR in this module and XMIT in CPSTT.ASM).
- ;edit 28, 30-Nov-1990 by MF. Modify routine "p20ln" to use "pausit" routine
- ; rather than explicitly checking for Console input to eliminate
- ; redundant code. Also fix spelling in "p20ln"'s comments.
- ;edit 27, 9-Sep-90 by MF. Put RET in routine PAUSIT per CPKERM.BWR.
- ; edit 26, September, 1987. Added pause-it routine to wait for a user keysroke.
- ;
- ; edit 25, August 19, 1987 by OBSchou. Fixed a few bugs here and there.
- ;
- ; edit 24, April 8, 1987 by OBSchou. Added routine to return one character
- ; from a section of several sectors worth of file. This routine needed
- ; for TRANSMIT.
- ;
- ; edit 23, March 11, 1986 by OBSchou for Richard Russell
- ; Bug in the TAKE code, such that a new sector was read in after 256
- ; bytes, and not the CPM value. A jnz is now jp in the test to see if
- ; the file buffer has bben exhausted. Many thanks for finding this
- ; bug. I have never used TAKE files more than 128 bytes long.
- ;
- ; edit 22, January 28, 1986 by OBSchou.
- ; split off the data areas from CPSUTL to CPSDAT.ASM (All in line
- ; with keeping individual files relatively small)
- ;
- ; edit 21 August, 1986 by OBSchou. Sorted a few more bugs in printer buffer
- ; etc. Have yet to try this with a real printer The code, apart from
- ; actually printing works OK.
- ;
- ; edit 26 20 August, 1986 by OBSchou for Godfrey Nix:
- ; edit 8-Aug-86 by Godfrey N. Nix [gnn] Nottingham University
- ; Added two extra bytes for storage of the send and receive
- ; start-of-packet characters. Used by CP4PKT, and altered
- ; by SET option in CP4MIT. Also added message strings for
- ; use by show routines. Added remote filename buffer and length byte.
- ;
- ; edit 11: June 20, 1986 by OBSchou. Added multi-fcbs for the DIR command
- ; together with some bug clearing and new routines. Had to move
- ; the overlay to 5000h as we ran out of space...
- ;
- ; edit 10: June 16, 1986 OBSchou. Added a pseudo clock and check for printer
- ; ready whenever one enters BDOS... This may slow things down a little
- ; but adds in (hopefully) pseudo background printing...
- ;
- ; edit 9 30-May-86 OBSchou. Added XON/XOFF routines here for the world
- ; at large to use. Also added two new entries in the overlay tables.
- ; One to give the address of the family of machines using the overlay,
- ; the other to the routine for giving printer status.
- ;
- ; edit 8: 27-May-86 OBSchou. Added code to check BDOS calls for info from
- ; the console. If so, and the take flag (takflg) is set then we
- ; substitute our own characters. Simple, a little tatty...
- ; Also added bits for SET CASE-SENSITIVE and SET FLOW=CONTROL, and
- ; removed the XMIT rubbish. This is a prelude to better TRANSMIT
- ;
- ; edit 7: 22 April, 1986 by OBSchou Lohghborough University
- ; Prlude to more changee, this time make overlay address to 4000h
- ; May revert back to ($+0ffh) AND 0ff00h as address for overlay.
- ; This gives us space to make quite a few modifications to the system
- ; dependent part without much fear of having to change this overlay
- ; address. Should also fix the Osborne problem of having to have io
- ; routines ii memory above 16k. I know I should not be introducing
- ; such system dependent rot here, but it wont be too difficult to fill
- ; memory to 4000h.
- ;
- ; edit 6: February 6, 1985
- ; Added a storage location for the port value (PORT, just below
- ; SPEED) which is used by the port status routine, and moved the
- ; printer copy flag (PRNFLG:) into the communications area so
- ; that the machine dependant overlay can toggle it. [Hal Hostetler]
- ; Added ffussy flag for filename checking. Generate the version
- ; string from 'verno', which is set in CP4KER, because CP4KER has the
- ; list of modules and their edit numbers. [Charles Carvalho]
- ;
- ; edit 5: 13-Jan-85 by Vanya J.Cooper Pima Commun. College Tel: 602-884-6809
- ;
- ;pcc002 28-Dec-84 modules:cp4tt,cp4utl
- ; Add connect mode <esc>P command to toggle printer on
- ; and off. Conflicts with "official" recommended commands
- ; in protocol manual, but I don't think CP/M will ever get
- ; a PUSH command.
- ;
- ;pcc003-pcc005 2-Jan-85 vjc modules:cp4mit,cp4tt,cp4utl
- ; These edits must all be installed together and change the way
- ; logging is handled. The log file spec is moved to a separate
- ; fcb, and not opened until an actual CONNECT command is given.
- ; This takes care of a NASTY bug that if you used any other file
- ; command between the LOG and CONNECT, the log file would get
- ; written over the last file used. This also allows logging to
- ; be "permanently" enabled until an CLOSE (new command) for all
- ; CONNECT sessions, like most other kermits do. If a log file
- ; already exists, it will be appended to. Also add two new
- ; CONNECT mode commands <esc>Q to suspend logging and <esc>R to
- ; resume. <esc>R means something else during TRANSMIT, but
- ; logging is never on then, so there shouldn't be any conflict.
- ; I also changed the write code, so that it can handle one more
- ; character after the XOFF is send to stop the host. This allows
- ; a little "slop" for systems that don't stop immediately (such
- ; as TOPS10), but it didn't help much.
- ;
- ;pcc006 2-jan-85 VJC modules:cp4cmd,cp4utl
- ; Problems with "?" in filespecs. On reparse, may cause action
- ; flag to be reset at wrong point, requiring multiple <CR>'s
- ; to terminate the line or other weird stuff. Also need to
- ; check flag and complain if wild-cards illegal.
- ;
- ;pcc008 2-Jan-85 vjc modules:cp4def,cp4tt,cp4utl
- ; Keyboard input during CONNECT mode can get locked out if
- ; there is enough input from the modem port to keep prtchr
- ; busy. This can happen for example, if the printer is running
- ; at the same speed as the modem line, leaving you helpless to
- ; turn it off or abort the host. Add a fairness count, so that
- ; at least every prfair characters we look at console input.
- ;
- ;pcc012 4-Jan-85 vjc modules:cp4mit,cp4tt,cp4utl
- ; Use the big buffer for the log file. Move the log file back
- ; into the common fcb and only save the drive, name, and
- ; extension between connects. Add new routines to cp4utl to
- ; create or append to an existing file, and to conditionally
- ; advance buffers only if in memory. Remove edit pcc003 that
- ; allows one more character after the xoff, since it didn't
- ; really work very well and does not fit in well with the way
- ; the buffer advancing routines are set up. If someone still
- ; thinks this would be useful, it could be put back in with a
- ; little more work.
- ;
- ; While testing this edit, I also noticed another bug that
- ; the command parsing routines do not limit or check the
- ; length of command lines or file specs, trashing what ever
- ; comes after them. Currently because of where the fcb and
- ; command buffer are located, this does not usually cause a
- ; problem, but could if an extremely long line was typed in,
- ; or in the future multiple fcbs defined elsewhere in memory
- ; were used. Maybe this should be put on the bug list
- ; somewhere.
- ;
- ;pcc013 8-Jan-85 vjc modules:cp4mit,cp4utl,cp4typ
- ; Replace CLOSE command to cancel session logging to SET
- ; LOGGING ON/OFF. This seems to fit in with the command
- ; structure better. Default the log file to KERMIT.LOG
- ; incase no previous LOG command. Logging is also enabled
- ; by LOG command, as before.
- ;
- ; edit 4: September 9, 1984
- ; Move command tables and associated help text to CP4MIT. Add
- ; makfil/clofil routines and modify outbuf to write files in big
- ; chunks. Update Kermit's version to 4.03.
- ;
- ; edit 3: August 21, 1984
- ; Make inbuf read files in big chunks to minimize disk start/stop
- ; delays. Buffer size and address is specified by system-dependent
- ; overlay.
- ;
- ; edit 2: August 3, 1984
- ; move "mover" to CP4SYS to allow use of Z80 block move instruction.
- ;
- ; edit 1: July 27, 1984
- ; extracted from CP4MIT.M80 edit 2, as part of LASM support. This is
- ; the last file linked for the system-independent code.
- ;
- utlver: db 'CPSUTL.ASM (31) 21-Mar-1991$'
-
- ; Set the parity for a character in A.
- ; called by: spack, rexmit, logit, vt52, conchr, intchr
-
- setpar: push h ;Save HL.
- push b
- lxi h,parity
- mov c,m ;Get the parity routine.
- mvi b,0
- lxi h,parjmp ;Get the first address.
- dad b
- pchl
-
- parjmp: jmp even
- jmp mark
- jmp none
- jmp odd
- jmp spacep
-
- none: jmp parret ;Don't touch the parity bit.
-
- even: ani 7FH ;Strip parity.
- jpe parret ;Already even, leave it.
- ori 80H ;Make it even parity.
- jmp parret
-
- mark: ori 80H ;Turn on the parity bit.
- jmp parret
-
- odd: ani 7FH ;Strip parity.
- jpo parret ;Already odd, leave it.
- ori 80H ;Make it odd parity.
- jmp parret
-
- spacep: ani 7FH ;Turn off the parity bit.
- jmp parret
-
- parret: pop b
- pop h ;Restore HL.
- ret
- ;
- ; Print the escape char.
- ; called by: stat01, telnet, intchr
-
- escpr: lda escchr ;Get the escape char.
- escpr1: cpi ' ' ;Is it a control char?
- jp escpr2
- push psw ; save the character
- lxi d,inms10 ;Output "Control-".
- call prtstr
- pop psw ; restore the character
- ori 100O ;De-controlify.
- escpr2: mvi c,conout ;Output the char
- mov e,a
- call bdos
- ret
-
- ; fetch keyword; if unsuccessful, return to command level.
- ; called by: kermit, setcom
-
- keycmd: mvi a,cmkey
- call comnd
- jmp keycm2 ;no match
- ret
-
- keycm2: lxi d,ermes1 ;"Unrecognized Command"
- call prtstr
- jmp kermit ;Do it again.
-
- ; request confirmation; if unsuccessful, return to command level
- ; called by: bye, exit, help, log, setcom, show, status, send,
- ; finish, logout, telnet
-
- cfmcmd: mvi a,cmcfm
- call comnd
- jmp kermt3 ;"Not confirmed"
- ret
- ;
-
- ; This routine prints the number in HL on the screen in decimal.
- ; Uses all ACs.
- ; called by: cp4sys, read, send, updrtr, dir, user
-
- nout: mvi a,'0' ; fill tempx with zeros
- call filltmp
-
- lxi b,-10 ;Get some useful constants.
- nout1: lxi d,-1
-
- nout2: dad b ;Subtract as many 10s as possible.
- inx d ;Count them.
- jc nout2 ;If some left keep going.
- push h ;save remainder - 10
- xchg ;Swap the remainder and the quotient.
- mov a,h ;Get the number of 10s found.
- ora l ;check for quotient non zero
- cnz nout1 ;If non zero, recurse.
- pop h ;Get the remainder - 10
- mov a,l ;in a
- adi '0'+10 ;Make the number printable and add the 10 back
- call shiftmp ; cycle temp registers
- sta temp1 ; and save digit
- mov e,a ;Output the digit.
- lda nquiet ; are we to be quiet?
- ana a
- rnz ; yup, so return here rather than frm bdos
- mvi c,conout
- jmp bdos
-
- ; prcrlf - print a CR/LF. (Saves no registers.) [Toad Hall]
- ; prtstr - print string pointed to by DE (now in overlay section.. see prtstx)
- ; called by: lots of places.
- prcrlf: lxi d,crlf ;Point to the CR/LF
- jmp prtstr ; Use the one in the overlay
-
- ; prtstx is funtionally the same as prtstr in the overaly but is here as
- ; we may need a print a string routine in case the overlay is
- ; either incorrect version or simply not present.
- prtstx: ; PRTSTR moved to overlay, but we do need the
- ; same function for writing the sign-on
- ; message and error message if the overlay
- ;is not present. Thence prtstx
- mvi c,prstr ; output string
- jmp bdos ;a CALL followed by a RET becomes a JMP
-
-
- ; Jumping to this location is like retskp. It assumes the instruction
- ; after the call is a jmp addr.
- ; here from: many places.
- rskp: pop h ;Get the return address.
- inx h ;Increment by three.
- inx h
- inx h
- pchl
-
- ; Jumping here is the same as a ret. 'jmp r' is used after routines
- ; that have skip returns, where the non-skip instruction must be 3 bytes.
- ; here from: many places.
- r: ret
-
- ; Pause-it routine. Informs the user to press any key to continue
- ; and then waits for a key input. Called by the any routine
- ; with more than, say, 20 lines of output.
-
- pausit: lxi d,anymes ; ask user to press any key to continue
- call prtstr
- pausi1: call ckcon ; see if a key typed
- ana a
- jz pausi1 ; loop until a key has been pressed.
-
- ret
-
- ;
- ; Open a file for reading (with inbuf). The filename is already
- ; in fcb; upon return, the end of file flag is cleared and chrcnt
- ; is set to zero, so inbuf will be called to get a buffer when we
- ; next attempt to get a character.
- ; called by: sinit, seof
-
- getfil: xra a
- sta chrcnt ;Buffer is empty.
- sta seccnt ;No sectors buffered.
- sta eoflag ;Not the end of file.
- sta endsts ;No EOF/error pending.
- sta fcb+0CH ;Zero the extent.
- sta fcb+0EH ;Must be zero for MAKEF or OPENF.
- sta fcb+20H ;Zero the current record.
- mvi c,openf ;Open the file.
- lxi d,fcb
- call bdos
- ret
-
- ; Get next sector. If necessary, read some more from disk.
- ; preserves bc, de, hl
- ; returns nonskip if EOF or error;
- ; returns skip with chrcnt and bufpnt updated if success.
- ; called by: gtchr, get1xc (from xmt/transmit)
-
- inbuf: lda eoflag ;Have we reached the end?
- ora a
- rnz ;Return if so.
- push b
- push d
- push h
- inbuf1: lda seccnt ; Do we have any sectors left?
- ora a
- jz inbuf3 ; If not, go get some more.
- inbuf2: lhld nxtbuf ; Yes. Get address of next sector
- shld bufpnt ; Update current buffer pointer
- lxi b,bufsiz ; Get number of bytes in sector
- dad b ; Update HL to point to next sector
- shld nxtbuf ; Save for next time
- dcr a ; Decrement count of buffered sectors
- sta seccnt ; Put it back
- mvi a,bufsiz-1 ; Number of bytes in buffer (pre-decremented)
- sta chrcnt ; Store for our caller
- pop h
- pop d
- pop b
- jmp rskp ; Return success
-
- ; We don't have any sectors buffered. If we've already hit an error or
- ; EOF, return that status to the user.
-
- inbuf3: lda endsts ; Check status from previous read
- ora a
- jz inbuf4 ; It was OK. Get some more sectors.
- sta eoflag ; End of file or error. Set the flag.
- xra a
- sta chrcnt ; Say no characters in buffer.
- pop h
- pop d
- pop b
- ret ; Return failure
-
- ; Read sectors until we fill the buffer or get an error or EOF, then return
- ; the first buffer to the user. (seccnt is zero)
-
- inbuf4: lhld bufadr ; Get address of big buffer
- shld nxtbuf ; Store as next buffer address to give user
- inbuf5: shld bufpnt ; Store as next buffer address to read into
- xchg ; Move buffer address to DE
- mvi c,setdma ; Tell CP/M where to put the data
- call bdos ; ...
- mvi c,readf ; Read a record.
- lxi d,fcb
- call bdos
- sta endsts ; Save ending status
- ora a ; 00H => read O.K
- jnz inbuf6 ; EOF/error: stop reading.
- lxi h,seccnt ; Success. Get addr of sector count
- inr m ; Bump sector count by one
- lda bufsec ; Get max number of sectors in buffer
- cmp m ; Are we there yet?
- jz inbuf7 ; Yes, can't read any more.
- lhld bufpnt ; No, try for another. Get buffer address,
- lxi d,bufsiz ; and size of sector,
- dad d ; giving next buffer address in HL
- jmp inbuf5 ; Go read another sector.
-
- ; We hit EOF or got an error. Put the DMA address back where it belongs,
- ; then go see if we have any sectors (before the one that got the error)
- ; to return to the caller. Nxtbuf points to the first good sector, if
- ; any; seccnt contains the count of good sectors.
-
- inbuf6: call rstdma
- jmp inbuf1 ; Go see if we have some data to return
-
- ; We've filled the big buffer. Reset the DMA address, then go return a
- ; sector to the caller. nxtbuf points to the beginning of the buffer;
- ; seccnt contains the number of sectors successfully read (except that
- ; if we've read 256 sectors, seccnt contains zero, so we can't just go
- ; to inbuf1).
-
- inbuf7: call rstdma ;[pcc012]
- lda seccnt ; Get sector count again.
- jmp inbuf2 ; Return a sector.
-
- ; IN1CHR - get a single character from the file. Taken code from old
- ; TRANSMIT routine.
- in1chr:
- lda eoflag ;EOF encountered?
- ora a
- rnz ; Yes, finish.
- lxi d,cmdbuf ; Use comnd buffer as line buffer.
- lhld bufpnt ; Get current buffer pointer.
- lda chrcnt ; Get current byte count
- mov b,a ; in B
- in1ch1: dcr b ; Assume there's a character there
- jp in1ch2 ; If there was, proceed.
- call inbuf ; There wasn't. Try for another buffer.
- jmp in1che ; End of file.
- lhld bufpnt ; Got another buffer. Get new pointer in HL
- lda chrcnt ; and new byte count
- mov b,a ; in B
- ;[MF]The modification below was made 30-Jan-1991 per report from
- ;[MF]Lance Tagliapietra from the University of Wisconsin at Platteville
- ;[MF]The following instruction should not be executed as the character counter
- ;[MF]has already been decremented by INBUF.
- ; dcr b ; we are reading in a character, so less one
- in1ch2: mov a,b ; save new count
- sta chrcnt
- mov a,m ; Get a character from disk buffer.
- inx h
- shld bufpnt ; save new pointer
- ani 7FH ; Mask 7 bits.
- jz in1ch1 ; Skip nulls.
- ret ; character in a
-
-
- in1che: mvi a,0ffh ; dubious about this one...
- sta eoflag
- ret ; set end of file flag...?????
-
- ;;[pcc012]
- ; appfil - Create or append to an existing file. File name is in FCB.
- ; Non-skip return if could not be done. Skip return with file
- ; open and bufpnt pointing to end of file.
- ; called by logopn
- appfil: xra a ;[pcc012] zero out stuff for open
- sta fcb+0CH ;[pcc012] extent
- sta fcb+0EH ;[pcc012] Must be zero for MAKEF or OPENF.
- sta fcb+20H ;[pcc012] Zero the current record.
- mvi c,openf ;[pcc012] Try to open the file
- lxi d,fcb ;[pcc012]
- call bdos ;[pcc012]
- cpi 0FFH ;[pcc012] Did we find it?
- jz makfi1 ;[pcc012] If not, go create it
- mvi c,cflsz ;[pcc012] Compute the file size
- lxi d,fcb ;[pcc012]
- call bdos ;[pcc012]
- lhld fcb+21H ;[pcc012] random record pointer
- mov a,h ;[pcc012] See if zero length file
- ora l ;[pcc012]
- jz makfi2 ;[pcc012] set up pointers if null file
- dcx h ;[pcc012] backup to last record written
- shld fcb+21H ;[pcc012] store rec ptr back
- lhld bufadr ;[pcc012] get buffer address
- xchg ;[pcc012] to DE
- mvi c,setdma ;[pcc012] set dma address
- call bdos ;[pcc012] for read
- mvi c,rrand ;[pcc012] read the last block
- lxi d,fcb ;[pcc012]
- call bdos ;[pcc012]
- ora a ;[pcc012] check results
- jnz rstdma ;[pcc012] reset dma and return if error
- lhld bufadr ;[pcc012] get address again
- lxi d,bufsiz ;[pcc012] and and size
- mvi a,'Z'-40H ;[pcc012] control-Z for comparison
- appcz: cmp m ;[pcc012] Is this the EOF?
- jz appxit ;[pcc012] Jump if yes
- inx h ;[pcc012] no, bump
- dcr e ;[pcc012] and grind
- jnz appcz ;[pcc012] until find or buffer empty
- appxit: shld bufpnt ;[pcc012] store buffer pointer
- dad d ;[pcc012] compute next buffer adr
- shld nxtbuf ;[pcc012] and store
- mov a,e ;[pcc012] updated chr count
- sta chrcnt ;[pcc012]
- xra a ;[pcc012] reset sector count
- sta seccnt ;[pcc012]
- call rstdma ;[pcc012] reset normal dma
- jmp rskp ;[pcc012] and give good return
-
- ; Create a file, deleting any previous version. The filename is in
- ; fcb.
- ; Returns nonskip if file could not be created.
- ; If successful, takes skip return with bufpnt and chrcnt initialized
- ; for output; buffers should be output via outbuf.
- ; called by: gofil
- makfil: mvi c,delf ; delete the file if it exists.
- lxi d,fcb
- call bdos
- xra a
- sta fcb+0CH ; zero the extent.
- sta fcb+0EH ; must be zero for MAKEF or OPENF.
- sta fcb+20H ; zero the current record.
- ;[pcc012] here from appfil above if file does not exist
- makfi1: mvi c,makef ;[pcc012] now create it.
- lxi d,fcb
- call bdos
- cpi 0FFH ; is the disk full?
- rz ; take error return if so.
- ; success. set up pointers and counters for multisector buffering.
- ;[pcc012] also here from appfil if found zero length file
- makfi2: lhld bufadr ;[pcc012] find beginning of buffer space.
- shld bufpnt ; make it current buffer.
- lxi d,bufsiz ; get sector size.
- dad d ; find beginning of next buffer.
- shld nxtbuf ; store for later.
- mov a,e ; store buffer size
- sta chrcnt ; for caller.
- xra a
- sta seccnt ; no sectors stored yet.
- jmp rskp ; take success return.
-
- ;[pcc012]
- ; outadv - conditionally advance output buffer if disk write not needed.
- ; preserves BC
- ; skip return with with next output buffer set up
- ; non-skip return if memory buffer full and must write to disk.
- ; called by:logit
-
- outadv: push b ;[pcc012] save BC as advertised
- lxi h,seccnt ;[pcc012] point to sectors buffered
- inr m ;[pcc012] count this one
- lda bufsec ;[pcc012] how many we can hold
- cmp m ;[pcc012] check if full
- jnz outbf2 ;[pcc012] continue if not
- dcr m ;[pcc012] full, un-advance sector count
- pop b ;[pcc012] restore bc
- ret ;[pcc012] and give non-skip return
-
- ; get a fresh output buffer, flushing big buffer if necessary.
- ; returns nonskip if disk full.
- ; if successful, returns skip with bufpnt and chrcnt updated. Note
- ; that chrcnt holds one less than the buffer size.
- ; preserves BC.
- ; called by: ptchr,logwrt
-
- outbuf: push b
- lxi h,seccnt ; count another buffered sector
- inr m ; ...
- lda bufsec ; get number of sectors we can hold
- cmp m ; full?
- jnz outbf2 ; if not, set up pointers and return
- call outmbf ; flush the big buffer
- jmp outbf9 ; disk error.
- ;[pcc012] also here from outadv to advance buffer
- outbf2: lhld nxtbuf ; get pointer to fresh buffer
- shld bufpnt ; store for caller
- lxi d,bufsiz ; advance our pointer to next buffer
- dad d
- shld nxtbuf
- mvi a,bufsiz-1 ; get buffer size (pre-decremented)
- sta chrcnt ; store for caller
- pop b
- jmp rskp ; return success.
-
- outbf9: pop b ; clean up stack
- ret ; and take error return.
-
- ; flush incore output buffers.
- ; returns nonskip if disk full.
- ; if successful, returns skip with nxtbuf reset to start of buffer and
- ; seccnt zero.
- ; destroys all ac's.
- ; called by: outbuf, clofil.
-
- outmbf: lhld bufadr ; get start of buffer
- shld nxtbuf ; store for next fill cycle
- shld bufpnt ; store for empty loop
- outmb2: lhld bufpnt ; get address of current sector
- xchg ; into DE
- lxi h,bufsiz ; advance HL to next sector
- dad d ; ...
- shld bufpnt ; and store for later
- mvi c,setdma
- call bdos ; point CP/M at current sector
- lxi d,fcb
- mvi c,writef
- call bdos ; output the sector
- ora a ; test for error (A non-zero)
- jnz rstdma ;[pcc012] reset dma and take nonskip return if so
- lxi h,seccnt
- dcr m ; count down buffered sectors
- jnz outmb2 ; loop if more saved
- call rstdma ;[pcc012] restore normal dma
- jmp rskp ; return success.
-
- ; output current buffer, flush incore buffers, and close output file.
- ; returns nonskip if disk full; skip if successful.
- ; called by: rdata
-
- clofil:
- lda chrcnt ; get the number of chars left in the buffer.
- cpi bufsiz ; Virgin buffer?
- jz clofl3 ; yes, don't output it.
- lhld bufpnt ; get the buffer pointer.
- clofl1: dcr a ; lower the count.
- jm clofl2 ; if full then stop.
- mvi m,'Z'-100O ; put in a ^Z for EOF.
- inx h ; point to the next space.
- jmp clofl1
-
- clofl2: call outbuf ; output the last buffer.
- jmp r ; give up if the disk is full.
- clofl3: lda seccnt ; any sectors buffered in memory?
- ora a
- jz clofl4 ; if not, don't try to flush.
- call outmbf ; flush buffers
- jmp r ; disk full.
- clofl4: mvi c,closf ; close up the file.
- lxi d,fcb
- call bdos
- jmp rskp ; return success.
-
-
- ; Reset DMA address to the default buffer
- ; called from inbuf,appfil,outmbf
- rstdma: lxi d,buff ;[pcc012]
- mvi c,setdma ;[pcc012]
- jmp bdos ;[pcc012]
-
- ; [8] Intercept BDOS calls to check for console input
- ; This leads to simple trapping for input from disk rather than from the
- ; keyboard, alowing commands to be stored in a TAKE file.
- ; Printer is tested for readiness, and the second fuzzy clock is updated.
- bdos: ;call print ; print a character to the printer if needed
- ;call clock ; update the clock
- push psw ; we will need this register
- lda takflg ; are we taking from a
- ana a ; file or from command line
- jz notake ; no, so do usual BDOS stuff
- mov a,c ; get bdos function
- cpi conin ; is it console in?
- jz bd1in ; get a single character
- cpi dconio ; direct console in or out?
- jz bd1io ; test further for inpu or output
- cpi rdstr ; read the console buffer?
- jz bdcbuf ; then do it
- cpi consta ; get the console status?
- jz bdcst ; anything left in buffer?
- notake: pop psw ; else we have a kosher BDOS call
- jmp 0005h ; Absolute address = BDOS entry point
- ;
- bd1in: ; get a single character from take file
- pop psw ; restore stack
- call r1tchr ; read a single take character
- ret ; and return. We dont expand tabs,
- ; check for xon/off or backspaces.
- ;Make sure the take file is error free?
- ;
- bd1io: ;get or put a single character from/to console
- mov a,e ; get e. If 0ffh then input else output
- inr a ; if 0 then input
- jnz notake ; its for output, so let notake restore stack
- pop psw ; otherwise we do it
- call r1tchr ; read a single take character
- ret ; and return from out BDOS
- ;
- bdcbuf: ; read a line of edited (?) input from the console
- pop psw ; restore stack
- inx d ; point to nc
- bdosc1: call r1tchr ; get a character
- cpi cr ; if a cr then return
- jz nomore
- cpi lf ; ignore line feeds
- jz bdosc1 ; so get another character
- push psw ; we will want it later
- mvi h,0
- ldax d ; get nc, the no of characters in buffer
- mov l,a ; now use as index from de+2
- mvi h,0
- inx d ; de is now de + 2
- dad d
- pop psw ; Told you we will want this
- mov m,a
- dcx d ; point again to nc
- xchg ; make hl point to memory
- inr m ; update pointer nc
- xchg ; restore it
- jmp bdosc1
- nomore: dcx d ; restore de to point to buffer
- ret
-
- bdcst: ; get the console status. Returns a 00 if at eof
- pop psw
- push h ; now save de, hl for return
- push d ;
- lxi d,takdma ; make a point to next byte...
- lhld takptr ; pointer from dma address. There will always
- dad d ; be at least one byte, as the buffer is
- mov a,m ; ...
- pop d ; filled only if a read empties it.
- pop h ; restore hl, de
- cpi cntlz ; end of file?
- mvi a,0ffh ; say there is
- rnz ; if it is not a cntl z
- jmp closet ; otherwise, close take file etc
-
- r1tchr: ; read a single character from the take file or command line
- push h
- push d ; save in case of return
- lda takflg ; see if character is to come from file or line
- ani 1 ; if bit zero set, from take file
- jz r1lchr ; get character from the command line
- lxi d,takdma
- lhld takptr ; get next data byte
- dad d
- mov a,m
- lhld takptr ; cos it's destroyed with dad
- inx h
- shld takptr ; update pointer
- call p1tchr ; print it (so the user sees it)
- push psw ; save the read data for a while
- mov a,l ; if l = 0 then read another sector
- ana a
- jp r1tch1 ;[23] was jnz. jp => 128 byte sectors
- call rnsect ;read next sector
- r1tch1: pop psw ; now, is this a cntl-z.. in whic case
- pop d ; also these...
- pop h
- cpi lf ; skip if a line feed
- jz r1tchr ;
- ;[MF][31][30]Following lines commented out so semicolons are not considered
- ;[MF][31][30]command separators and thus are considered part of the command
- ;[MF][31][30]so commands like REMOTE DELETE *.*;* work correctly.
- ; cpi semico ; see if its a semicolon
- ; jnz r1tch2 ; no, ignore it
- ; mvi a,cr ; else say its a cr (in case of command lines)
- r1tch2: cpi cntlz ; end of file??
- rnz
-
-
- c1tchr: call closet ; close file etc, then
- mvi a,cr ; fake a carriage return chr
- ; ( => clears kermit comnd line)
- c1tch1: ret ; and hope that editing etc not required.
-
- r1lchr: ; read a single character from the command line
- lxi d,cbuff ; point to buffer
- lda cbptr ; get pointer for next character
- mov l,a
- lda cbuff ; get total number of characters there
- cmp l ; ... less current character
- jp r1lch1 ; if positive, we have more characters
- lda takflg ; no more, so reset command line bit (bit 4)
- ani 0efh
- sta takflg
- mvi a,cntlz ; fudge an end of file
- push psw ; save for common exit (r1tch1)
- jmp r1tch1
- ;
- r1lch1: mov a,l ; get count back again
- mvi h,0
- dad d ; get offset to character
- inr a
- sta cbptr
- mov a,m ; get next character
- cpi semico ; if a semicolon, make it a carriage return
- jnz r1lch2
- mvi a,cr
- r1lch2: call p1tchr ; send a copy to the console
- push psw ; save it for r1tch1
- jmp r1tch1 ; common exit
-
-
- ; rnsect - read the next take sector from disk to the take dma address
- ; if there is no more then close the file too
- rnsect: push b
- push d
- push h ; save in case we need these later
- mvi c,setdma
- lxi d,takdma ; set a next read from disk
- call bdos ; recursive...
- mvi c,readf
- lxi d,takfcb
- call bdos
- ana a
- cnz closet ; if returned value not zero, assume eof
- lxi h,0
- shld takptr ; pointer restored
- call rstdma ; reset the dma address for fussy routines (this one)
- pop h
- pop d
- pop b
- ret
-
- ; closet - close the take file and set the take flag to 0 (ie no takes)
- ;
- closet: lda takflg ; reset the take file bit (bit 0)
- ani 0feh
- sta takflg ; close the take file, and restore the flag
- mvi c,closf
- lxi d,takfcb
- call bdos
- call rstdma ; in case we did not do it above, reset dma
- ret
-
- ;
- ; clock - is a 32 bit counter incremented every BDOS call. It serves as a
- ; timer of sorts and allows a background clock to tick away..
- clock: push psw ; we need flags and hl
- push h
- lhld clkbit ; get the counter
- inx h ;
- shld clkbit
- mov a,h
- ora l ; do we need to update the next lot of clock bits?
- jnz clockx
- lhld clkbit+2 ; if carry up the top 16 bits
- inx h
- shld clkbit+2
- clockx: pop h
- pop psw
- ret
-
- ; p1tchr - print a character in accumulator directly to the console
- ; bypassing the bdos trap above.
-
- p1tchr: cpi lf ; if a lf ignore it
- jz p1tchx
- cpi cr ; ditto carriage returns
- jz p1tchx
- cpi cntlz ; control z
- jz p1tchx ; then dont write it out
- push psw ; we do not want to loose it, do we?
- push b
- push d
- push h ; 'cos you never know what bdos does...
- mov e,a
- mvi c,conout ; direct console io
- call 5 ; absolute address as we skip the trap
- pop h
- pop d
- pop b ; ... and we need some of these regs.
- pop psw ;
- p1tchx: ret ; and return
-
-
-
- ; outprn - This routine sends charactes to the printer if the latter is ready,
- ; or to a buffer if the printer is not ready. If the buffer is nearly
- ; full, an XOFF is sent to the host, asking it to be quiet. The buffer
- ; is emptied by a series of calls in the connect state only.
- ; If the buffer is made nearly empty, then an XON is sent to the host.
- ;
- outprn: mov a,e ; get the character to send back to a
- sta prntmp ; we need all registers.
- jmp outprx ; -testing-testing-testing- avoid buffer
- ;
- outp0: call tstfree ; see how many spaces free
- cpi 2 ; (free spaces in a on return)
- jp outp1 ; enough free spaces, so keep going
- call print ; else see if we can print summat
- jmp outp0 ; and try again
- ;
- outp1: cpi 4 ; common test - if three or less then send xoff
- cm sndxoff
- outpr2: mov a,b ; inc ptr and check for wrap around
- call wrapt
- mov b,a ; input pointer to b
- sta prnbuf+1 ; save the new pointer away
- lxi h,prnbuf+2 ; point to first real data entry in buffer
- call inchl ; add offset in a to hl
- lda prntmp ; get th character to save away
- mov m,a ; save the data away
- ret
-
- ; outprx - send character in a to the printer. (We have checked to see if
- ; the printer is ready)
- ; called by outprn, print
- outprx: mov e,a ; character has to be in e register
- call outlpt ; send it to printer
- ret ; assume we print it
-
- ; TSTFREE - see how many free spacse there are in the buffer
- ; - returns with free space in a, ip pointer in b, op pointer in c
- szecyc equ 127 ; 128 bytes in buffer (less for debugging)
- ;
- tstfree:
- lda prnbuf ; get output pointer
- mov c,a ;.. to c
- lda prnbuf+1 ; and input pointer ...
- mov b,a ; ... to b
- ;
- ;
- ; Now comes the tricky bit. We must establish whether there is less than
- ; three characters left in the buffer. There are two conditions to test for
- ; 1) the input pointer is a higher value than the output pointer
- ; 2) the input pointer has been wrapped round and is less than the output pointer
- ; ie
- ; |-------|-------|---------------------------------------------|
- ; Buffer |o/p ptr|i/p ptr| Buffer proper filling ---> |
- ; |-------|-------|------|-------------|-----------|------------|
- ; i/p2 o/p i/p1
- ;
- ; If ip = ip1 then if
- ; (size of buffer - ip ptr + op ptr) < 3 send xoff
- ; If ip = ip2 then if
- ; (op ptr - input ptr ) < 3 send xoff
- ;
- ; First decide whice one applies
-
- mov a,b ; get ip ptr
- sub c ; see if op ptr > ip ptr (case 2)
- jm outp2 ; yup, so do case two
- mvi a,szecyc ; else do buffer - ip + op
- sub b
- add c
- jmp outpx ; do common test
- outp2: mov a,c ; get op pointer
- sub b ; less input pointer
- outpx: ret ; with free space in a
- ;
- ;
- ;
- ; print - get a character from the buffer and print it if the printer
- ; is ready for it. If the buffer clears more than 3 spare characters
- ; and an xoff has been sent, then send an xon again.
- print: push h ; save for rainy days
- push d
- push b
- push psw ; .. as we may need flags etc....
- lda initflg ; First check if the system has initialised
- ana a
- jz printx ; If system not set up then skip
- ; nop
- ; nop
- ; nop ; debugging only...
- call ckprtr ; check to see if printer is ready...
- ana a ; not zero => ok
- jz printx ; else skipit.
- ; nop
- ; nop
- ; nop ; skip the jump for debugging
- lxi h,prnbuf
- mov a,m ; get input pointer
- inx h ; test against output pointer
- cmp m ; if = then buffer empty
- jz printx ; so quit
- dcx h ;pointer to output pointer
- call wrapt ; check for wrap around
- sta prnbuf ; save new pointer
- inx h
- inx h ;
- call inchl ; add output pointer to hl
- mov c,m ; get byte
- lda hosths ; have we told host to be quiet?
- cpi xoff ; if = xoff then we have
- jnz print1 ; nope, so just print it..
- push b ; save the character to print
- call tstfree ; see how many free bytes in buffer
- pop b
- cpi 4 ; 3 characters left?
- jz printx
- push b
- call sndxon ; send an xon to host and wake it up.
- pop b
- print1: mov a,c ; we are gonna print a character, so ...
- call outprx ; get it to a (as required by outprx) and print it
- printx:
- pop psw
- pop b
- pop d
- pop h ; restore regs.
- ret
-
- ;
- ; Utilities for the cyclical buffer. Returns a 0ffh if printer ready,
- ; else 0h. Called by outprn, print
-
- ckprtr:
- call lptstat ; no registers saved
- ; mvi a,0 ; FOR DEBUGGING PURPOSES
- ; nop
- ret
- ;
-
- inchl: push psw ; we do maths through this register
- add l
- mov l,a
- mvi a,0
- adc h
- mov h,a
- pop psw ; hl = a + hl
- ret
-
- ; wrapt - checks the offset in a with the limits of the buffer.
- ; returns next address or if wrap around then 0 (start of buffer)
- wrapt: push b
- inr a
- mov b,a ; save new a into b for now
- mvi a,szecyc ; test for size of buffer
- sub b
- mov a,b
- pop b ; restore bc regs again
- rnz
- xra a ; if wrap around, then reset pointer
- ret ; return with next address pointer to in a
-
- ; sndxoff - send an xoff to the host and save the xoff character in hosths
- ; saves all regs. is called by logwrt, outprn
- sndxoff:
- push psw
- push b
- push d
- push h ; some calling routines may be sensitive...
- lda floctl ; are we doing flow control?
- ana a
- jz sndxf ; no, so dont bother.
- mvi a,xoff ;^S to stop the host while we write the buffer.
- sta hosths ; save it so we know we have sent it
- call setpar ; set correct parity...
- mov e,a
- call outmdm ; output it.
- lxi d,ofsnt ; say we have sent an xoff
- call prtstr
- sndxf: pop h
- pop d
- pop b
- pop psw ; some routines touchy
- ret
- ofsnt: db cr,lf,'[XOFF sent to host]',cr,lf,'$'
-
- ; sndxon - send an xon to the host and clear the hosths flag. saves everything
- ; called by logwrt, print
- sndxon: push psw
- push b
- push d
- push h
- lda floctl ; are we doing flow control?
- ana a
- jz sndxn
- xra a
- sta hosths ; no xoff to hos any more
- mvi a,xon ;^Q to restart the host
- call setpar ; set appropriate parity
- mov e,a
- call outmdm ; send it.
- lxi d,onsnt
- call prtstr ; say xon sent to host...
- sndxn: pop h
- pop d
- pop b
- pop psw
- ret ; shame we dont do a pushall/popall subroutine...
- onsnt: db cr,lf,'[XON sent to host]',cr,lf,'$'
- ;
- ; Routines to clear (or rather fill) TEMPnnn space with the data in A
- ; and to shift it all along one (filltmp an shiftmp respectively)
- filltmp:
- push b
- push d
- push h ; save all
- lxi h,temp1
- mvi b,10 ; ten locations to fill
- fillp: mov m,a
- inx h
- dcr b ; loop til all done
- jnz fillp
- pop h
- pop d
- pop b ; restore all
- ret
-
- shiftmp:
- push psw ; save all again
- push b
- push d
- push h
- lxi d,temp9
- lxi h,temp10
- mvi b,9 ; shift nine times
- shiftl: ldax d ; from tempx
- mov m,a ; to tempx+1
- dcx d
- dcx h ; mover does not work as that increments
- dcr b
- jnz shiftl
- pop h
- pop d
- pop b
- pop psw
- ret ; else all done
-
- ; getun - get the user number to temp1 (lsd) and temp2 (msd)
- ;
- getun: mvi a,0ffh ; tell nout to be quiet
- sta nquiet
- mvi c,usrcod
- mvi e,0ffh ; get current user from bdos
- call bdos
- mov l,a ; put into hl
- mvi h,0
- call nout ; decimalise it (decimalise???)
- xra a
- sta nquiet ; let nout print again
- ret
-
- ; ckcon - Do a direct console IO (read) to see if there is any input
- ; returns with a=0 (no input) or character (input received)
- ; Assume that all regs may be destroy.
- ckcon: mvi e,0ffh ; direct console input
- mvi c,dconio
- call bdos
- ret ; and return with wahtever returned in a
-
-
-
- ;
- ; subbc - Subtract the unsigned number in bc from the unsigned number
- ; in HL with the answer in HL. Flags altered, all
- ; other registers left intact.
- subbc: sta temp1 ; hl = hl - bc.. we need the accumulator
- mov a,l
- sub c
- mov l,a
- mov a,h
- sbb b
- mov h,a
- lda temp1 ; restore loop counter but not flags
- ret
-
- ; P20LN - Routine to print a string at (DE) and count the number of
- ; line feeds. Pause after 20 lines printed.
- p20ln: xra a ; clear the line counter
- sta lincnt
- p20ln1: ldax d ; get character to print
- inx d
- cpi '$' ; if a dollar we have done
- rz
- push d
- push psw ; save pointer and character to print
- mov e,a
- call outcon ; send character
- pop psw
- pop d ; restore pointers etc
- cpi lf ; was that last character a line feed?
- jnz p20ln1 ; no, so carry on
- lda lincnt ; yup, so update counter
- inr a
- sta lincnt
- cpi 20 ; 20 lines printed?
- jnz p20ln1 ; not yet
- push d ; we need DE
- ; lxi d,anymes ; pause a while [MF]removed
- ; call prtstr ; write the message [MF]
- ;p20ln2: call ckcon ; wait for any input [MF]
- ; ana a ;[MF]
- ; jz p20ln2 ;[MF]
- call pausit ;[MF] pause a while
- pop d
- jmp p20ln ; and continue
-
-
- ; Little code to allow some expansion of code without changing
- ; every futher address, only up to the end of this file.
- ; TO BE REMOVED FOR RELEASE!
-
- ; org ($+100h) AND 0FF00H
-
- ; link to the data area (was part of CPSUTL.ASM)
-
- IF lasm
- LINK CPSDAT
- ENDIF ;lasm
-